package proxy

import (
	"os"
	"os/exec"
	"bytes"
	"strconv"
	"runtime"
	"gitee.com/lidt/proxy-ui/common/utils/logger"
	"gitee.com/lidt/proxy-ui/systemcfg"
	"gitee.com/lidt/proxy-ui/dao/defaults"
	"gitee.com/lidt/proxy-ui/dao/global"
	"gitee.com/lidt/proxy-ui/dao/frontend"
	"gitee.com/lidt/proxy-ui/dao/backend"
	"strings"
	"gitee.com/lidt/proxy-ui/dao/backend_servers"
)

func Init() {
	log.Info("开始初始化proxy服务")
	if !Exists(systemcfg.CONFIG_PATH) {
		err := os.MkdirAll(systemcfg.CONFIG_PATH, 0711)
		if err != nil {
			log.Fatal("config path create failed err--> ", err)
		}
	}

	cfgPath := systemcfg.CONFIG_PATH + systemcfg.CONFIG_FILE

	initProxyCfg(cfgPath)

	log.Info("启动proxy服务")
	//execShell(systemcfg.PROXY_EXECUTOR + " -f " + cfgPath + " -sf $(cat /var/run/haproxy.pid)")
	execShell(systemcfg.PROXY_EXECUTOR + " -f " + cfgPath)
	log.Info("启动proxy服务成功, proxy初始化完成")
}
func execShell(s string) {
	log.Info("exec shell => /bin/bash -c " + s)
	cmd := exec.Command("/bin/bash", "-c", s)
	var out bytes.Buffer

	cmd.Stdout = &out
	if err := cmd.Run(); err != nil {
		log.Fatal("启动proxy服务失败 err--> ", err)
	}

	log.Info(out.String())
}
func initProxyCfg(cfgPath string) {
	log.Info("开始初始化proxy的配置文件")
	baseCfg := getBaseCfg()
	userCfg := getUserCfg()

	file, err := os.Create(cfgPath)
	if err != nil {
		log.Fatal("open file ", cfgPath, " failed, err--> ",  err)
	}

	file.WriteString(baseCfg + userCfg)
	log.Info("初始化proxy配置文件成功")
}
func getUserCfg() string {
	userCfg := ""
	frontends := frontend.GetAll()
	if len(frontends) == 0 {
		return ""
	}

	for _, frontend := range frontends{
		item := "\n"
		item += "frontend " + frontend.Name + "\n"
		item += "\t bind *:" + strconv.Itoa(frontend.Bind_port) + "\n"
		item += "\t mode " + frontend.Mode + "\n"
		item += "\t default_backend " + frontend.Backend + "\n"

		backend := backend.GetByName(frontend.Name)
		if backend == nil {
			continue
		}

		item += "backend " + backend.Name + "\n"
		item += "\t balance " + backend.Balance + "\n"

		server_ids := strings.Split(backend.Servers, "|")
		if len(server_ids) == 0 {
			continue
		}

		serverstr := ""

		for _, server_id := range server_ids{
			id, err := strconv.Atoi(server_id)
			if err != nil {
				continue
			}
			server := backend_servers.GetById(id)
			if server == nil {
				continue
			}

			serverstr += "\t server " + server.Name + " " +
				server.Host + ":" + strconv.Itoa(server.Port) +
				" check port " + strconv.Itoa(server.Port) +
				" intval 2 rise 1 fall 2 maxconn " + strconv.Itoa(server.Maxconn) + "\n"
		}

		if serverstr == "" {
			continue
		}

		item += serverstr
		item += "\n"

		userCfg += item
	}

	return userCfg
}
func getBaseCfg() string {
	globalCfg := getGlobalCfg()
	defaultsCfg := getDefaultsCfg()
	adminCfg := getAdminCfg()

	return globalCfg + defaultsCfg + adminCfg
}
func getAdminCfg() string {
	return `
frontend default
    mode	http
    option	httpclose
    bind	0.0.0.0:5000
    acl 	haproxy_stats path_beg /haproxy
    use_backend	haproxy_stats if haproxy_stats

backend haproxy_stats
    mode	http
    stats	uri /haproxy
    stats	enable
    stats	refresh 60s
    stats	auth admin:admin
    stats	admin if TRUE
`
}
func getDefaultsCfg() string {
	defaultsCfg := defaults.GetOne()
	mode := "\tmode\t" + defaultsCfg.Mode + "\n"
	retries := "\tretries\t" + strconv.Itoa(defaultsCfg.Retries) + "\n"
	timeout_connect := "\ttimeout connect\t" + defaultsCfg.Timeout_connect + "\n"
	timeout_client := "\ttimeout client\t" + defaultsCfg.Timeout_client + "\n"
	timeout_server := "\ttimeout server\t" + defaultsCfg.Timeout_server + "\n"
	timeout_check := "\ttimeout check\t" + defaultsCfg.Timeout_check + "\n"

	return "defaults\n" + mode + retries + timeout_connect + timeout_client + timeout_server + timeout_check
}
func getGlobalCfg() string {
	globalCfg := global.GetOne()
	chroot := "\tchroot\t" + globalCfg.Chroot + "\n"
	log := "\tlog\t" + globalCfg.Log + "\n"
	maxconn := "\tmaxconn\t" + strconv.Itoa(globalCfg.Maxconn) + "\n"
	nbproc := "\tnbproc\t" + strconv.Itoa(runtime.NumCPU()) + "\n"
	user := "\tuser\t" + globalCfg.User + "\n"
	group := "\tgroup\t" + globalCfg.Group + "\n"
	daemon := "\n"
	if globalCfg.Daemon {
		daemon = "\tdaemon\n"
	}
	pidfile := "\tpidfile\t" + globalCfg.Pidfile + "\n"

	return "global\n" + chroot + log + maxconn + nbproc + user + group + daemon + pidfile
}
// 判断所给路径文件/文件夹是否存在
func Exists(path string) bool {
	_, err := os.Stat(path)    //os.Stat获取文件信息
	if err != nil {
		if os.IsExist(err) {
			return true
		}
		return false
	}
	return true
}
